﻿using System;
using System.Collections.ObjectModel;
using System.Linq;

namespace Creventive.Wpf.ShellFactory.Utility
{
    /// <summary>
    ///   Represents a collection which can be sealed.
    /// </summary>
    /// <typeparam name = "T"></typeparam>
    public class SealableCollection<T> : Collection<T>, ISealable
    {
        private bool isSealed = false;

        /// <summary>
        ///   Gets a value indicating whether this collection has been sealed.
        /// </summary>
        /// <value><c>true</c> if this collection has been sealed; otherwise, <c>false</c>.</value>
        public bool IsSealed
        {
            get { return this.isSealed; }
        }

        /// <summary>
        ///   Clears all items from the collection.
        /// </summary>
        protected override void ClearItems()
        {
            this.VerifyUnsealed();
            base.ClearItems();
        }

        /// <summary>
        ///   Inserts the item at the specified index.
        /// </summary>
        /// <param name = "index">The index.</param>
        /// <param name = "item">The item.</param>
        protected override void InsertItem(int index, T item)
        {
            this.VerifyUnsealed();
            base.InsertItem(index, item);
        }

        /// <summary>
        ///   Removes the item at the specified index.
        /// </summary>
        /// <param name = "index">The index.</param>
        protected override void RemoveItem(int index)
        {
            this.VerifyUnsealed();
            base.RemoveItem(index);
        }

        /// <summary>
        ///   Sets the item at the specified index.
        /// </summary>
        /// <param name = "index">The index.</param>
        /// <param name = "item">The item.</param>
        protected override void SetItem(int index, T item)
        {
            this.VerifyUnsealed();
            base.SetItem(index, item);
        }

        /// <summary>
        ///   Verifies that the collection is unsealed.
        /// </summary>
        protected void VerifyUnsealed()
        {
            if (this.isSealed)
                throw new InvalidOperationException("Cannot modify sealed object");
        }

        /// <summary>
        ///   Seals this collection and any sealable items.
        /// </summary>
        public void Seal()
        {
            this.Seal(true);
        }

        /// <summary>
        ///   Seals the collection, optionally also sealing any sealable items.
        /// </summary>
        /// <param name = "sealChildren">if set to <c>true</c> [seal children].</param>
        public void Seal(bool sealChildren)
        {
            if (!this.isSealed)
            {
                this.OnSealing();
                if (sealChildren)
                    this.OfType<ISealable>().ForEach(s => s.Seal());
                this.isSealed = true;
            }
        }

        /// <summary>
        ///   Called when the collection is being sealed.
        /// </summary>
        protected virtual void OnSealing()
        {}
    }
}